package cn.virens.common.project.base.utils;

import cn.virens.common.util.exception.APIException;
import cn.virens.common.util.exception.ExceptionUtil;
import org.dromara.hutool.core.date.DateUtil;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Optional;

public class TimeUtil {
    public static final String DATE_F = "MM-dd";
    public static final String TIME_F = "HH:mm";

    private static final LocalDate DEF_DAY = LocalDate.of(2000, 1, 1);

    private static final DateTimeFormatter FM_DATE = DateTimeFormatter.ofPattern(DATE_F);
    private static final DateTimeFormatter FM_TIME = DateTimeFormatter.ofPattern(TIME_F);

    private static String check(String v) {
        if (v != null && v.length() > 5) {
            return v.substring(0, 5);
        } else {
            return v;
        }
    }

    /**
     * 计算时间差(单位:毫秒)
     */
    public static long diff(long last) throws APIException {
        return System.currentTimeMillis() - last;
    }

    /**
     * 计算时间差(单位:毫秒)
     */
    public static long diff(LocalDateTime last) throws APIException {
        return diff(last.atZone(zone()).toEpochSecond() * 1000);
    }

    /**
     * 判断当前是不是上午
     */
    public static boolean isAM8(Date time) throws APIException {
        return time != null && isAM8(toLocalTime(time));
    }

    /**
     * 判断当前是不是上午
     */
    public static boolean isAM8(LocalTime time) throws APIException {
        return time != null && time.getHour() <= 12;
    }

    /**
     * 将UTC时间戳转换为Date对象
     */
    public static Date parseUTC(String date) throws APIException {
        try {
            if (date != null && !date.isEmpty()) {
                return DateUtil.parse(date);//
            }
        } catch (Exception e) {
        }

        return null;
    }

    /**
     * 将UTC时间戳转换为Date对象(Optional)
     */
    public static Optional<Date> parseUTC1(String date) throws APIException {
        return Optional.ofNullable(parseUTC(date));
    }

    /**
     * 将时间以{h, m}方式转为byte数组
     */
    public static byte[] toByteByHM(Date date) {
        return toByteByHM(toLocalTime(date));
    }

    /**
     * 将时间以{h, m}方式转为byte数组
     */
    public static byte[] toByteByHM(LocalTime date) {
        byte b2 = (byte) date.getMinute();
        byte b1 = (byte) date.getHour();

        return new byte[]{b1, b2};
    }

    /**
     * 将秒时间戳转化为byte数组
     */
    public static byte[] toByteByUnix(long time) {
        byte[] result = new byte[8];

        result[7] = (byte) ((time >> 0) & 0xff);
        result[6] = (byte) ((time >> 8) & 0xff);
        result[5] = (byte) ((time >> 16) & 0xff);
        result[4] = (byte) ((time >> 24) & 0xff);
        result[3] = (byte) ((time >> 32) & 0xff);
        result[2] = (byte) ((time >> 40) & 0xff);
        result[1] = (byte) ((time >> 48) & 0xff);
        result[0] = (byte) ((time >> 56) & 0xff);

        return result;
    }

    /**
     * 将毫秒时间戳转化为byte数组
     */
    public static byte[] toByteByUnixNow() throws APIException {
        return toByteByUnix(System.currentTimeMillis() / 1000);
    }

    /**
     * 判断是否在时间范围
     */
    public static boolean timeIn(Date now, Date beginDate, Date endDate) throws APIException {
        return timeIn(toLocalTime(now), toLocalTime(beginDate), toLocalTime(endDate));
    }

    /**
     * 判断是否在时间范围
     */
    public static boolean timeIn(LocalTime now, LocalTime beginDate, LocalTime endDate) throws APIException {
        return beginDate.isBefore(endDate) ? in1(now, beginDate, endDate) : in2(now, beginDate, endDate);
    }

    /**
     * 判断是否在时间范围（开始时间大于结束时间）
     */
    private static boolean in2(LocalTime now, LocalTime beginDate, LocalTime endDate) {
        return now.isAfter(beginDate) || now.isBefore(endDate);
    }

    /**
     * 判断是否在时间范围（开始时间小于结束时间）
     */
    private static boolean in1(LocalTime now, LocalTime beginDate, LocalTime endDate) {
        return now.isAfter(beginDate) && now.isBefore(endDate);
    }

    /**
     * 计算两个时间戳的时间差
     */
    public static long between(Date d1, Date d2) throws APIException {
        return between(toLocalTime(d1), toLocalTime(d2));
    }

    /**
     * 计算两个时间戳的时间差
     */
    public static long between(LocalTime d1, LocalTime d2) throws APIException {
        long t1 = d1.getHour() * 3600 + d1.getMinute() * 60;
        long t2 = d2.getHour() * 3600 + d2.getMinute() * 60;

        return t2 - t1;
    }

    /**
     * 获取当前日期(MM-dd)
     */
    public static String date() throws APIException {
        return date(DateUtil.now());
    }

    /**
     * 获取指定星期(week*)
     */
    public static String week(LocalDate d) throws APIException {
        return d != null ? "week" + d.getDayOfWeek().getValue() : "";
    }

    /**
     * 获取指定星期(week*)
     */
    public static String week(LocalDateTime d) throws APIException {
        return d != null ? "week" + d.getDayOfWeek().getValue() : "";
    }

    /**
     * 获取指定日期(MM-dd)
     */
    public static String date(long d) throws APIException {
        return DateUtil.format(new Date(d), DATE_F);
    }

    /**
     * 获取指定日期(MM-dd)
     */
    public static String date(Date d) throws APIException {
        return DateUtil.format(d, DATE_F);
    }

    /**
     * 获取指定日期(MM-dd)
     */
    public static String date(LocalDate d) throws APIException {
        return FM_DATE.format(d);
    }

    /**
     * 获取指定日期(MM-dd)
     */
    public static String date(LocalDateTime d) throws APIException {
        return FM_DATE.format(d);
    }

    /**
     * 转换MM-dd日期为LocalDate
     */
    public static LocalDate date(String d) throws APIException {
        return ExceptionUtil.safe3(check(d), (v) -> {
            return LocalDate.from(FM_DATE.parse(v));
        });
    }

    /**
     * 获取当前时间(HH:mm)
     */
    public static String time() throws APIException {
        return time(DateUtil.now());
    }

    /**
     * 获取指定时间(HH:mm)
     */
    public static String time(long d) throws APIException {
        return DateUtil.format(new Date(d), TIME_F);
    }

    /**
     * 获取指定时间(HH:mm)
     */
    public static String time(Date d) throws APIException {
        return DateUtil.format(d, TIME_F);
    }

    /**
     * 获取指定时间(HH:mm)
     */
    public static String time(LocalTime d) throws APIException {
        return FM_TIME.format(d);
    }

    /**
     * 获取指定时间(HH:mm)
     */
    public static String time(LocalDateTime d) throws APIException {
        return FM_TIME.format(d);
    }

    /**
     * 转换HH:mm日期为LocalTime
     */
    public static LocalTime time(String d) throws APIException {
        return ExceptionUtil.safe3(check(d), (v) -> {
            return LocalTime.from(FM_TIME.parse(v));
        });
    }

    /**
     * 获取对应的纪元秒
     */
    public static long timestmap(Date date) throws APIException {
        return timestmap(toLocalDateTime(date));
    }

    /**
     * 获取对应的纪元秒
     */
    public static long timestmap(LocalTime date) throws APIException {
        return timestmap(date.atDate(DEF_DAY));
    }

    /**
     * 获取对应的纪元秒
     */
    public static long timestmap(LocalDate date) throws APIException {
        return timestmap(date.atStartOfDay());
    }

    /**
     * 获取对应的纪元秒
     */
    public static long timestmap(LocalDateTime date) throws APIException {
        return date.atZone(zone()).toEpochSecond();
    }

    /**
     * java.util.Date --> java.time.LocalDate
     */
    public static LocalDate toLocalDate(Date v) throws APIException {
        return v == null ? null : toLocalDateTime(v).toLocalDate();
    }

    /**
     * java.util.Date --> java.time.LocalTime
     */
    public static LocalTime toLocalTime(Date v) throws APIException {
        return v == null ? null : toLocalDateTime(v).toLocalTime();
    }

    /**
     * java.util.Date --> java.time.LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(Date v) throws APIException {
        return v == null ? null : LocalDateTime.ofInstant(v.toInstant(), zone());
    }

    /**
     * java.time.LocalDate --> java.util.Date
     */
    public static Date formLocalDate(LocalDate v) throws APIException {
        return v == null ? null : formLocalDateTime(v.atStartOfDay());
    }

    /**
     * java.time.LocalTime --> java.util.Date
     */
    public static Date formLocalTime(LocalTime v) throws APIException {
        return v == null ? null : formLocalDateTime(v.atDate(DEF_DAY));
    }

    /**
     * java.time.LocalDateTime --> java.util.Date
     */
    public static Date formLocalDateTime(LocalDateTime v) throws APIException {
        return v == null ? null : Date.from(v.atZone(zone()).toInstant());
    }

    /**
     * 获取当前时区
     */
    private static ZoneId zone() throws APIException {
        return ZoneId.systemDefault();
    }
}
